<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传下载</title>
    <style>
        #app {
            font-family: Avenir, Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #2c3e50;
            margin-top: 60px;
        }

        #progress-bar {
            width: 300px;
            height: 20px;
            background-color: #f0f0f0;
            margin: 10px auto;
            position: relative; /* 添加相对定位 */
        }

        #progress {
            height: 100%;
            background-color: #4CAF50;
            width: 0%;
        }

        #progress-bar span {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-weight: bold;
        }
    </style>
</head>
<body>
<div id="app">
    <h1>文件上传下载</h1>
    <input type="file" @change="uploadFile">
    <div id="progress-bar">
        <div id="progress" :style="{width: progress + '%'}"></div>
        <span>{{ progress }}%</span> <!-- 添加进度百分比显示 -->
    </div>
    <button @click="downloadFile">下载文件</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/spark-md5@3.0.0/spark-md5.min.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            progress: 0,
            fileMD5: null, // Store the MD5 value here
            //  文件分块大小 前端传递一个呗。
            fileSize: 200,
            chunkSize: 1048576, // 1MB
        },
        methods: {
            uploadFile: function (e) {
                const file = e.target.files[0];
                if (!this.fileMD5) { // If MD5 is not already calculated
                    this.calculateMD5(file, (md5) => {
                        this.fileMD5 = md5; // Store the MD5 value for future use
                        this.check(file);
                    });
                } else {
                    this.check(file); // Use the stored MD5 value
                }
            },
            check: function (file) {
                const formData = new FormData();
                formData.append('fileName', file.name);
                formData.append('fileSize', file.size);
                formData.append('fileMd5', this.fileMD5);
                fetch('http://localhost:8004/upload/check', {
                    method: 'POST',
                    body: formData
                })
                    .then(response => response.text())
                    .then(data => {
                        if (data === '1') {
                            alert('文件已存在，无需上传');
                            this.progress = 100; // 设置进度条为100%
                            return;
                        } else {
                            alert('文件不存在，执行分块上传流程');
                            const totalChunks = Math.ceil(file.size / this.chunkSize); // 每片 1MB
                            this.uploadChunks(file, totalChunks);
                        }
                    })
            },
            uploadChunks: function (file, totalChunks) {
                let uploadedChunks = 0;

                for (let i = 0; i < totalChunks; i++) {
                    const start = i * this.chunkSize;
                    const end = Math.min((i + 1) * this.chunkSize, file.size);
                    const chunk = file.slice(start, end);

                    const chunkFormData = new FormData();
                    chunkFormData.append('file', chunk);
                    chunkFormData.append('chunkNumber', i + 1);
                    chunkFormData.append('totalChunks', totalChunks);
                    chunkFormData.append('fileName', file.name);
                    chunkFormData.append('fileMd5', this.fileMD5);

                    fetch('http://localhost:8004/upload/chunk', {
                        method: 'POST',
                        body: chunkFormData
                    }).then(response => response.text())
                        .then(chunkData => {
                            uploadedChunks++;
                            this.progress = parseFloat(((uploadedChunks / totalChunks) * 100).toFixed(2)); // 格式化为两位小数
                            if (uploadedChunks === totalChunks) {
                                this.mergeChunks(file.name, totalChunks);
                            }
                        });
                }
            },
            mergeChunks: function (fileName, totalChunks) {
                const mergeFormData = new FormData();
                mergeFormData.append('fileName', fileName);
                mergeFormData.append('totalChunks', totalChunks);
                mergeFormData.append('fileMd5', this.fileMD5);

                fetch('http://localhost:8004/upload/merge', {
                    method: 'POST',
                    body: mergeFormData
                }).then(response => response.text())
                    .then(mergeData => {
                        alert(mergeData);
                    });
            },
            downloadFile: function () {
                const fileName = 'example.txt'; // 替换为实际文件名
                fetch('http://localhost:8004/download/' + fileName)
                    .then(response => response.blob())
                    .then(blob => {
                        const url = window.URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = url;
                        a.download = fileName;
                        document.body.appendChild(a);
                        a.click();
                        a.remove();
                        window.URL.revokeObjectURL(url);
                    });
            },
            calculateMD5(file, callback) {
                const chunkSize = this.chunkSize; // 使用统一的 chunkSize
                const spark = new SparkMD5.ArrayBuffer();
                let offset = 0;

                const reader = new FileReader();
                reader.onload = function (e) {
                    spark.append(e.target.result); // Append array buffer
                    offset += e.target.result.byteLength;
                    if (offset < file.size) {
                        loadNext();
                    } else {
                        callback(spark.end());
                    }
                };
                reader.onerror = function () {
                    console.error('File read error');
                    callback(null);
                };

                function loadNext() {
                    const slice = file.slice(offset, offset + chunkSize);
                    reader.readAsArrayBuffer(slice);
                }

                loadNext();
            }
        }
    })
</script>
</body>
</html>
